package com.w3c.pragmatic.utils;

import ch.qos.logback.core.util.CloseUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.w3c.pragmatic.common.enums.ErrorTypeEnum;
import com.w3c.pragmatic.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 文件操作工具类
 *
 * @author wangdong
 * @since 2025/3/20 9:33
 */
@Slf4j
@Component
public class FileUtil {

  // public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
  public static final String SYS_TEM_DIR = "C:\\upload\\";

  /**
   * 下载网络文件
   *
   * @param urlFilePath 源文件地址(本地文件)
   * @param targetFileName 目标文件名称(下载的文件名称)
   * @param httpServletResponse 响应
   */
  public void downloadUrlFile(
      String urlFilePath, String targetFileName, HttpServletResponse httpServletResponse) {
    HttpURLConnection urlConnection = null;
    try {
      // 文件转成流
      URL urlFile = new URL(urlFilePath);
      urlConnection = (HttpURLConnection) urlFile.openConnection();
      /*urlConnection.setConnectTimeout(5000);
      urlConnection.setReadTimeout(10000);*/
      InputStream inputStream = urlConnection.getInputStream();
      this.downloadFile(inputStream, targetFileName, httpServletResponse);
    } catch (IOException e) {
      // 创建文件流失败
      log.error("downloadFile文件下载异常:", e);
      throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "远程文件获取失败！");
    } finally {
      // 关闭连接
      if (ObjectUtil.isNotNull(urlConnection)) urlConnection.disconnect();
    }
  }

  /**
   * 下载本地文件
   *
   * @param localFilePath 源文件地址(本地文件)
   * @param targetFileName 目标文件名称(下载的文件名称)
   * @param httpServletResponse 响应
   */
  public void downloadLocalFile(
      String localFilePath, String targetFileName, HttpServletResponse httpServletResponse) {
    try {
      // 文件转成流
      File localFile = new File(localFilePath);
      FileInputStream fileInputStream = new FileInputStream(localFile);
      this.downloadFile(fileInputStream, targetFileName, httpServletResponse);
    } catch (FileNotFoundException e) {
      // 创建文件流失败
      log.error("downloadFile文件下载异常:", e);
      throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "文件不存在！");
    }
  }

  /**
   * 通过IOUtils以流的形式下载
   *
   * @param inputStream 输入流
   * @param targetFileName 目标文件名称(下载的文件名称)
   * @param httpServletResponse 响应
   */
  public void downloadFile(
      InputStream inputStream, String targetFileName, HttpServletResponse httpServletResponse) {
    try {
      // 文件名编码格式
      targetFileName = URLEncoder.encode(targetFileName, "UTF-8");
      // 设置下载文件属性
      httpServletResponse.setHeader(
          "Content-disposition", "attachment;filename*=utf-8' '" + targetFileName);
      TimeUnit.SECONDS.sleep(10);
      IOUtils.copy(inputStream, httpServletResponse.getOutputStream());
      httpServletResponse.flushBuffer();
    } catch (Exception e) {
      log.error("downloadFile文件下载异常:", e);
      throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "文件下载异常！");
    } finally {
      if (ObjectUtil.isNotNull(inputStream)) {
        try {
          inputStream.close();
        } catch (IOException e) {
          log.error("downloadFile文件流关闭异常:", e);
        }
      }
    }
  }

  /**
   * 文件上传
   *
   * @param multipartFile 源文件
   * @param storageFileName 存储文件名称
   * @param storagePath 存储文件路径
   */
  public void uploadFile(MultipartFile multipartFile, String storageFileName, String storagePath) {
    // 拼接存储文件全路径
    String filePath = storagePath + storageFileName;
    // 构建文件对象(如果不存在则递归创建路径)
    File file = new File(filePath);
    // file.getParentFile().mkdirs()和file.mkdirs()区别
    // file.mkdirs()===>"/path/to/dir" 会创建/path/to/dir
    // file.getParentFile().mkdirs()===>"/path/to/dir/file.txt" 会创建/path/to/dir, file.txt作为文件不会被创建
    if (!file.getParentFile().exists())
      if (!file.getParentFile().mkdirs())
        throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "文件上传创建路径失败！");
    // 将文件写到指定位置
    try {
      multipartFile.transferTo(file);
    } catch (IOException e) {
      log.error("uploadFile文件上传异常:", e);
      throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "文件上传异常！");
    }
  }

  /**
   * 获取文件扩展名 https://www/ww/姓名.txt 或 c:\\姓名.txt
   *
   * @param filename 文件名
   * @return 扩展名 .pdf/.txt
   */
  public String getExtensionName(String filename) {
    if (StrUtil.isNotEmpty(filename)) {
      int dot = filename.lastIndexOf(StringPool.DOT);
      if ((dot > -1) && (dot < (filename.length() - 1))) {
        // return filename.substring(dot + 1); // 不含.
        return filename.substring(dot); // 包含.
      }
    }
    return filename;
  }

  /**
   * 获取文件名称 https://www/ww/姓名.txt 或 c:\\姓名.txt
   *
   * @param fileName 路径名称
   * @return 文件名称 姓名.txt
   */
  public String getFileName(String fileName) {
    // 全名称为空直接返回
    if (StrUtil.isEmpty(fileName)) return fileName;
    // 兼容windows和unix环境
    int lastUnixPos = fileName.lastIndexOf(StringPool.SLASH);
    int lastWindowsPos = fileName.lastIndexOf(StringPool.BACK_SLASH);
    int index = Math.max(lastUnixPos, lastWindowsPos);
    return fileName.substring(index + 1);
  }

  /**
   * MultipartFile转File
   *
   * @param multipartFile multipartFile对象
   * @return File对象
   */
  public File multipartToFile(MultipartFile multipartFile) {
    // 获取文件后缀名
    String fileName = multipartFile.getOriginalFilename();
    String suffix = this.getExtensionName(fileName);
    try {
      // 用uuid作为文件名，防止生成的临时文件重复
      File file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + suffix);
      // file.getParentFile().mkdirs()和file.mkdirs()区别
      // file.mkdirs()===>"/path/to/dir" 会创建/path/to/dir
      // file.getParentFile().mkdirs()===>"/path/to/dir/file.txt" 会创建/path/to/dir, file.txt作为文件不会被创建
      if (!file.getParentFile().exists())
        if (!file.getParentFile().mkdirs())
          throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "文件上传创建路径失败！");
      multipartFile.transferTo(file);
      // 返回file对象
      return file;
    } catch (IOException e) {
      log.error("MultipartFile转File异常:", e);
      throw new BusinessException(
          ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "MultipartFile转File异常！");
    }
  }

  /**
   * inputStream转File
   *
   * @param inputStream 文件输入流
   * @param name 文件名称
   * @return File对象
   */
  public File inputStreamToFile(InputStream inputStream, String name) {
    // 文件存在直接返回对象
    File file = new File(SYS_TEM_DIR + name);
    if (file.exists()) return file;
    // 处理文件流
    OutputStream outputStream = null;
    try {
      outputStream = new FileOutputStream(file);
      int bytesRead;
      int len = 4096;
      byte[] buffer = new byte[len];
      while ((bytesRead = inputStream.read(buffer, 0, len)) != -1)
        outputStream.write(buffer, 0, bytesRead);
      // 返回文件对象
      return file;
    } catch (Exception e) {
      log.error("InputStream转File异常:", e);
      throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "InputStream转File异常！");
    } finally {
      if (ObjectUtil.isNotNull(outputStream)) {
        try {
          outputStream.close();
        } catch (IOException e) {
          log.error("InputStream转File文件输出流关闭异常:", e);
        }
      }
      if (ObjectUtil.isNotNull(inputStream)) {
        try {
          inputStream.close();
        } catch (IOException e) {
          log.error("InputStream转File文件输入流关闭异常:", e);
        }
      }
    }
  }

  /**
   * 读取文件内容(字符流，仅支持文本文件)
   *
   * <p>支持格式：txt/csv/json/xml/properties/ini/java/py/html/css/js/log
   *
   * <p>不支持格式：docx/doc/xlsx/xls/pdf/jpg/mp4/avi/zip...
   *
   * @param filePath 文件路径
   * @return 文件内容 String
   */
  public String readFileToString(String filePath) {
    StringBuilder content = new StringBuilder();
    BufferedReader reader = null;
    try {
      reader = new BufferedReader(new FileReader(filePath));
      // 按行读取
      String line;
      while ((line = reader.readLine()) != null)
        content.append(line).append(System.lineSeparator());
      // 返回文件字符串
      return content.toString();
    } catch (Exception e) {
      log.error("readFileToString异常:", e);
      throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "readFileToString异常！");
    } finally {
      if (ObjectUtil.isNotNull(reader)) {
        try {
          reader.close();
        } catch (IOException e) {
          log.error("BufferedReader流关闭异常:", e);
        }
      }
    }
  }

  /**
   * 读取文件内容为字节数组
   *
   * @param filePath 文件路径
   * @return 文件内容 byte[]
   */
  public byte[] readFileToByteArray(String filePath) {
    try {
      File file = new File(filePath);
      return Files.readAllBytes(file.toPath());
    } catch (Exception e) {
      log.error("readFileToByteArray异常:", e);
      throw new BusinessException(
          ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "readFileToByteArray异常！");
    }
  }

  /**
   * 写入文件内容(字符流:操作文本) *
   *
   * <p>支持格式：txt/csv/json/xml/properties/ini/java/py/html/css/js/log
   *
   * <p>不支持格式：docx/doc/xlsx/xls/pdf/jpg/mp4/avi/zip...
   *
   * @param filePath 文件路径
   * @param strContent 写入的内容
   */
  public void writeStringToFile(String filePath, String strContent) {
    // 按路径创建对象，如果不存在递归创建目录
    File file = new File(filePath);
    // file.getParentFile().mkdirs()和file.mkdirs()区别
    // file.mkdirs()===>"/path/to/dir" 会创建/path/to/dir
    // file.getParentFile().mkdirs()===>"/path/to/dir/file.txt" 会创建/path/to/dir, file.txt作为文件不会被创建
    if (!file.getParentFile().exists())
      if (!file.getParentFile().mkdirs())
        throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "文件创建路径失败！");
    // 创建文件对象字符写入对象
    BufferedWriter writer = null;
    try {
      writer = new BufferedWriter(new FileWriter(filePath));
      writer.write(strContent);
    } catch (Exception e) {
      log.error("writeStringToFile异常:", e);
      throw new BusinessException(
          ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "writeStringToFile异常！");
    } finally {
      if (ObjectUtil.isNotNull(writer)) {
        try {
          writer.close();
        } catch (IOException e) {
          log.error("BufferedWriter流关闭异常:", e);
        }
      }
    }
  }

  /**
   * 将字节数组写入文件
   *
   * @param filePath 文件路径
   * @param byteContent 字节文件内容
   */
  public void writeByteArrayToFile(String filePath, byte[] byteContent) {
    // 按路径创建对象，如果不存在递归创建目录
    File file = new File(filePath);
    // 目录不存在则需要创建
    if (!file.getParentFile().exists())
      if (!file.getParentFile().mkdirs())
        throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "文件创建路径失败！");
    // 使用工具类写入对象
    try {
      Files.write(file.toPath(), byteContent);
    } catch (Exception e) {
      log.error("writeByteArrayToFile异常:", e);
      throw new BusinessException(
          ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "writeByteArrayToFile异常！");
    }
  }

  /**
   * 复制文件
   *
   * @param sourceFilePath 源文件路径
   * @param destFilePath 目标文件路径
   */
  public void copyFile(String sourceFilePath, String destFilePath) {
    try {
      Path source = Paths.get(sourceFilePath);
      Path dest = Paths.get(destFilePath);
      Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);
    } catch (IOException e) {
      log.error("copyFile异常:", e);
      throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "copyFile异常！");
    }
  }

  /**
   * 遍历目录获取所有文件(叶子结点)
   *
   * @param directory 路径
   * @return 所有文件
   */
  public List<File> getFileList(String directory) {
    List<File> fileList = new ArrayList<>();
    File directoryFile = new File(directory);
    if (directoryFile.isDirectory()) {
      File[] files = directoryFile.listFiles();
      if (files != null) {
        for (File file : files) {
          if (file.isDirectory()) fileList.addAll(getFileList(file.getAbsolutePath()));
          else fileList.add(file);
        }
      }
    }
    return fileList;
  }

  /**
   * 删除文件
   *
   * @param filePath 文件路径
   */
  public void deleteFile(String filePath) {
    try {
      Path path = Paths.get(filePath);
      Files.deleteIfExists(path);
    } catch (IOException e) {
      log.error("deleteFile异常:", e);
      throw new BusinessException(ErrorTypeEnum.BUSINESS_ERROR.getErrCode(), "deleteFile异常！");
    }
  }
}
